/*
 * This file is part of Space Invaders: Rehash
 * Copyright (C)2007 Vicent Marti (tanoku@gmail.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "SDL.h"
#include "SDL_ttf.h"
#include "SDL_opengl.h"

#include "main.h"
#include "gfx.h"
#include "font_handler.h"
#include "cutscene.h"
#include "game_const.h"

std::string act_names[] =
{
	"ACT 1: A NOT-SO-NEW THREAT", //act 1 0
	"ACT 2: URBAN PIXEL GUERRILLA", //act 2 // 5
	"ACT 3: DIAL P FOR PRIMES", // act 3 // 10
	"ACT 4: OUR LAST HOPE" //act 4 // 15
};

// TODO: jeez these suck... ask somebody funnier than me for new titles
std::string level_names[] =
{
	"1. STARTING OUT SOFTLY",
	"2. I THINK I'VE PLAYED THIS GAME BEFORE",
	"3. LOOK! IT'S A NEW GAME MECHANIC",
	"4. BOUNCING LIKE... BOOBS?",
	"1. OH GOD! THEY HAVE GONE 8-BIT!", //
	"2. ARR - GEE - BEE",
	"3. STEVIE WONDER COULD NOT BEAT THIS",
	"4. PIXELS IN THE SKY WITH DIAMONDS",
	"1. PRE-SCHOOL FACTORIZATION", 
	"2. YOU SON OF AN EULER",
	"3. PRIMALITY TESTS",
	"4. P OBVIOUSLY EQUALS NP",
	"1. THE SPRITES STRIKE BACK",
	"2. ", // ??
	"3. DEATH BY FACTORIZATION",
	"4. A FAIR MEETING"
};


// TODO: make this shit funnier, goddamnit...
// FIXME: typo checking?
std::string level_text[] =
{
	//--------------------------------------------------
	// ACT 1
	//--------------------------------------------------
	// level 1
	"Greetings, Soldier.\n"
	"Our planet is being attacked by evil 2-bit sprites from the planet TWODEE,\n"
	"and it is your mission to defend it by repeatedly firing missiles from a\n"
	"magically hovering cannon. Remember to use both arrow keys to move, and\n"
	"the space bar to shoot. Kill the damn aliens before they get to you!",
	
	// level 2
	"Good job, soldier!\n"
	"I bet you are are thinking that was some easy shit, huh? Well, be careful\n"
	"because the evil sprites are now shooting small 2D squares which surprisingly\n"
	"act as bombs. Thats why we have strategically placed some barriers on top\n"
	"of you. Try to dodge the bombs anyway, hmmkay?",
	
	// level 3
	"Still rocking, huh?\n"
	"Well, things are going to get much tougher later on, so you might as well\n"
	"learn to place the damn barriers before the game starts. Thats a feat that\n"
	"will come in handy, trust me.",
	
	// level 4
	"Wake up already!\n"
	"Still thinking this is way too easy? Well, lets see what happens now that\n"
	"your missiles are bouncing off the sky. Thats a few more stuff to dodge.",
	
	//--------------------------------------------------
	// ACT 2
	//--------------------------------------------------
	// level 1
	"Jeez... We werent expecting that.\n"
	"The aliens have developed a new kind of defence: They are paiting themselves\n"
	"of different colors so our white missiles don't hurt them.\n"
	"Fortunately, we have modified your canon so it can shoot colored missiles too.\n"
	"Match the alien color with your missile in order to destroy him! It is not\n"
	"that hard, really... plus we have removed some aliens to make it easier.",
	
	// level 2
	"Good job.\n"
	"You are already getting the grasp of it, arent you? Well, this time around\n"
	"make sure you shoot the right colored missile, because otherwise it is going\n"
	"to bounce back. And you dont want that. Trust me.",
	
	// level 3
	"Alrighty, you are doing fine.\n"
	"The aliens are regrouping and increasing in number, though.\n"
	"Fortunately you have some shields to place again.",
	
	// level 4
	"This alien scum never learns.\n"
	"They have even more colours now: purple and brown aliens are joining the\n"
	"attack, so make sure you keep your hands on your 5th and 6th key.\n"
	"On a related topic, we are running low on resources and you only have\n"
	"two barriers to place.",
	
	//--------------------------------------------------
	// ACT 3
	//--------------------------------------------------
	// level 1
	"Oops... Looks like we have a problem.\n"
	"The aliens have replaced their color shields with composite numbers!\n"
	"Once again, we have tweaked your cannon so it shoots prime numbers:\n"
	"factorize their shields until they disappear, then shoot a normal\n"
	"missile to finish them off.",
	
	// level 2
	"Good job Mr Euler.\n"
	"I bet you can still beat those guys even if they increase their shields\n"
	"with numbers up to 100. Same rules apply, just remember that pressing\n"
	"the (1) key shoots a prime missile, only useful for factorizing a\n"
	"strictly prime number higher than 17.",
	
	// level 3
	"Nice again.\n"
	"They are increasing their numbers now, but their shields dont get\n"
	"higher than 100 yet. You will have to factorize much faster, though.\n"
	"...and no, you dont have any barriers yet.",
	
	// level 4
	"This is your final prime challenge.\n"
	"Numbers are getting really high now, up to 500, and there is still a\n"
	"good bunch of aliens to fight. Remember that thing about the best\n"
	"defence being a good attack? Well, we tweaked your canon so you can\n"
	"fire twice as fast now. It would be extremely ironic, though, if you\n"
	"blew yourself up with your own missiles. Watch it.",
	
	//--------------------------------------------------
	// ACT 4
	//--------------------------------------------------
	// level 1
	"Finally, some change...\n"
	"We are back to old Space Invaders mode, but trust me, you have never\n"
	"seen sprites like these before. It is going to take two shots to kill\n"
	"each alien, and there are a ton of them to kill.\n"
	"...at least we placed some barriers down for you this time. Do not whine.\n",
	
	//level 2
	"Yikes, more colors.\n"
	"Try to focus, because these colored aliens now take two shots each, too,\n"
	"and there is a big bunch of them.\n"
	"Did I mention you now have to use all 8 colors? Oh, the joy.",
	
	// level 3
	"I bet you must be having nightmares involving prime numbers and 2D sprites\n"
	"by now, huh? Well, here are some more just for your enjoyment.\n"
	"Tons of aliens. Tons of primes. There is no shooting, there are no barriers.\n"
	"Just plain old mental math in a crazy time-attack.",
	
	// level 4
	"All good things come to an end.\n"
	"The president of PLANET TWODEE and leader of the invasion has accepted\n"
	"having a meeting with you. Try to convince him to surrender, and if it\n"
	"does not work, kill him!\n"
	"Oh, and if it works, kill him too. Where are we gonna lock a 300m wide\n"
	"alien, anyway?"
};

extern int level_rules[];

// FIXME: some of these descriptions don't fit on screen... careful with newlines, damnit
const char *getRuleDesc( int rule, bool full = false )
{
	switch ( rule )
	{
	case GR_PRIMESHOOTER:
		return "PRIME SHOOTER:\nShoot prime missiles to remove the aliens number shields.\nUse keys 1-9,Q,W,E,R,T.";
	case GR_COLORSHOOTER:
		return "COLOR SHOOTER:\nShoot colored missiles to kill aliens.\nUse keys 1-8.";
	case GR_MISSILES_BOUNCE_ON_TOP:
		return "MISSILES BOUNCE ON TOP:\nWatch your aim! If you don't hit any aliens,\nyour missile will bounce back once it reaches the sky...";
	case GR_MISSILES_BOUNCE_ON_ENEMIES:
		return "MISSILES BOUNCE ON ENEMIES:\nCareful now! Any missile that doesnt damage an\nenemy will bounce back and hurt you.";
	case GR_PLACE_DEFENCES_2:
		return "PLACE 2 BARRIERS:\nThis time you can also choose the position of your defence\nbut be careful: you only have 2 barriers at your disposal.";
	case GR_PLACE_DEFENCES_4:
		return "PLACE 4 BARRIERS:\nYou are now allowed to choose where do you want to place your\ndefences before the game starts. Choose wisely.";
	case GR_STATIC_DEFENCES:
		return "STATIC DEFENCES:\nThree concrete barriers are placed at the beggining of the game\nto help you in your defence against the invaders.";
	case GR_LOW_ENEMY_COUNT:
		return "LOW ENEMY COUNT:\nLets start off with a few aliens...\nAt least until you get used to destroying their shields..";
	case GR_MED_ENEMY_COUNT:
		return "MEDIUM ENEMY COUNT:\nYou are lucky, there seems to be slightly less enemies this time.";
	case GR_HIGH_ENEMY_COUNT:
		return "HIGH ENEMY COUNT:\nThere are quite a few aliens ";
	case GR_TOUGH_ENEMIES:
		return "TOUGH ENEMIES:\nLooks like these evil sprites have been taking some\nserious steroid doses. It now takes 2 shots to kill them!";
	case GR_FAST_ENEMIES:
		return "FAST ENEMIES:\nA caffeine overdose is making the aliens run fast as fuck.\nActually, they just run at twice the normal speed.";
	case GR_FAST_SHOOTING:
		return "FAST SHOOTING:\nEnjoy the rof you.\neduced time between your attacks!\n...but try not to kill yourself with bouncing missiles.";
	case GR_ENEMIES_DONT_ATTACK:
		return "ENEMIES DONT ATTACK:\nYou are lucky: the aliens arent attacking you yet.\nEnjoy while it lasts, and practice your aim.";
	case GR_PORTAL:
		return "PORTAL MODE:\nAperture Science Inc. has kindly donated one of\ntheir portal devices, which has been placed at both sides of the field.";
	case GR_GODMODE:
		return "CHUCK NORRIS MODE:\nChuck Norris factorizes 8000-digit long composite\nnumbers in constant time. He is also obviously immortal.";
	}

	return "NO DESC";
}

CutScene::CutScene(int level_id, int seen_rules)
{
	std::string str = level_text[level_id];

	m_levelid = level_id;
	m_seenrules = seen_rules;

	m_totalChars = 0;
	m_drawnLines = 0;
	m_pauseType = false;
	m_skipping = false;
	m_finished = false;
	
	m_typet = new SimpleTimer();
	
	using namespace std;
	size_t p0 = 0, p1 = string::npos;
	
	while(p0 != string::npos)
	{
		p1 = str.find_first_of("\n", p0);
		if(p1 != p0)
		{
			string token = str.substr(p0, p1 - p0);
			m_textLines.push_back(token);
		}
		p0 = str.find_first_not_of("\n", p1);
	}
	
	m_skm = "PRESS (SPACE) TO SKIP";
	
	SDL_Rect r;
	g_Fonts->get(FONT_TEXT)->textSize( m_skm, &r );
	
	m_skm_x = (SCREEN_W/2) - (r.w/2);
	m_skm_y = SCREEN_H - 15 - r.h;
}

void CutScene::drawAllRules()
{
	int gr, newRule = 0, count = 0; char str[64];

	for ( int i=0; i<31; ++i )
	{
		if ( i >= 14 && i <= 17 )
			continue;

		if ( i == 29 || i == 28 )
			continue;

		gr = (1<<i);

		if ( i >= 18 && i <= 20 && ( level_rules[m_levelid] & gr ) && ( level_rules[m_levelid] & GR_COLORSHOOTER ) )
		{
			sprintf(str, "PLAYING COLOR SHOOTER MODE WITH UP TO %d DIFFERENT COLORS", 4 + (i-18)*2);
			glPrint(FONT_TEXT, 150, 170, str );
		}
		else if ( i >= 22 && i <= 25 && ( level_rules[m_levelid] & gr ) && ( level_rules[m_levelid] & GR_PRIMESHOOTER ) )
		{
			int num;
			switch(i)
			{
			case 22: num = 20; break;
			case 23: num = 100; break;
			case 24: num = 500; break;
			case 25: num = 1000; break;
			}

			sprintf(str, "PLAYING PRIME SHOOTER MODE WITH NUMBERS UP TO %d", num);
			glPrint(FONT_TEXT, 150, 170, str );
		}
		else if ( level_rules[m_levelid] & gr )
		{
			drawGameRule(count, gr, false);
			count++;
			
			if ( ( m_seenrules & gr ) == 0 )
			{
				drawGameRule( 0, gr, true );
				newRule = gr;
			}
		}
	}

	if ( count > 0 )
	{
		glPrint(FONT_TEXT, 32, 225 - 16, "ACTIVE GAME RULES:" );
	}
	
	if ( newRule > 0 )
	{
		glPrint(FONT_ALIEN, 150, 80, "NEW GAME RULE!" );
		glPrint_br(FONT_TEXT, 150, 110, getRuleDesc( newRule, true ) );
	}
}

void CutScene::drawGameRule( int count, int gamerule, bool isNew )
{
	int xc, yc;
	switch(gamerule)
	{
	case GR_ENEMIES_DONT_ATTACK:		
		xc = 0; yc = 0; break;
	case GR_FAST_SHOOTING:				
		xc = 1; yc = 0; break;
	case GR_PORTAL:						
		xc = 2; yc = 0; break;
	case GR_GODMODE:					
		xc = 3; yc = 0; break;
	case GR_FAST_ENEMIES:				
		xc = 0; yc = 1; break;
	case GR_TOUGH_ENEMIES:				
		xc = 1; yc = 1; break;
	case GR_MISSILES_BOUNCE_ON_ENEMIES: 
		xc = 2; yc = 1; break;
	case GR_MISSILES_BOUNCE_ON_TOP:		
		xc = 3; yc = 1; break;
	case GR_STATIC_DEFENCES:			
		xc = 0; yc = 2; break;
	case GR_LOW_ENEMY_COUNT:			
		xc = 1; yc = 2; break;
	case GR_MED_ENEMY_COUNT:			
		xc = 2; yc = 2; break;
	case GR_HIGH_ENEMY_COUNT:			
		xc = 3; yc = 2; break;
	case GR_PRIMESHOOTER:				
		xc = 0; yc = 3; break;
	case GR_COLORSHOOTER:				
		xc = 1; yc = 3; break;
	case GR_PLACE_DEFENCES_2:			
		xc = 2; yc = 3; break;
	case GR_PLACE_DEFENCES_4:			
		xc = 3; yc = 3; break;
	default: return;
	}

	float w = 1.0/4.0f;
	float size = isNew?100.0f:64.0f;

	glLoadIdentity();

	if ( isNew )
	{
		glTranslatef( (GLfloat)32, (GLfloat)80, 0 );
	}
	else
	{
		glTranslatef(  32 + count*(size+8), (GLfloat)225, 0 );
	}

	glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
	glBindTexture(GL_TEXTURE_2D, g_Textures[14]);
	
	glBegin( GL_QUADS );
		glTexCoord2f( xc*w, yc*w + w );		glVertex3f( 0.0f,	0.0f,	0.0f );
		glTexCoord2f( xc*w + w, yc*w + w ); glVertex3f( size,	0.0f,	0.0f );
		glTexCoord2f( xc*w + w, yc*w );		glVertex3f( size,	size,	0.0f );
		glTexCoord2f( xc*w, yc*w );			glVertex3f( 0.0f,	size,	0.0f );
	glEnd();
	
	glBindTexture (GL_TEXTURE_2D, 0);
}

void CutScene::tick()
{
	if ( !m_skipping && g_Keys[KSHOOT] )
	{
		m_totalChars = 1000;
		m_skipping = true;
		m_pauseType = false;
	}
	
	render();
}

void CutScene::onEvent( SDL_Event *event )
{
}

void CutScene::render()
{	
// TODO: this is the kind of unmaintenable code that is begging for a complete
// rewrite... actually I should just scrap the whole class, what was I thinking
// when I wrote this, seriously?

// FIXME: ok, maybe this doesn't suck THAT much if i fix the code for skipping
// the cutscene...
	glEnable2D();
	glBindTexture (GL_TEXTURE_2D, 0);
	glDisable(GL_BLEND);

	int totalchars = m_totalChars;
	std::string out;
	
	if ( !m_typet->isStarted() || !m_pauseType && m_typet->get_ticks() > CHARACTER_DELAY )
	{
		m_totalChars++;
		m_typet->start();
	}
	else if ( m_pauseType && m_typet->get_ticks() > NEWLINE_DELAY )
	{
		m_totalChars++;
		m_typet->start();
		m_pauseType = false;
	}
	
	int x = 32, y = 320, h = g_Fonts->get(FONT_CUTSCENE)->getHeight();
	for ( int i=0; i<(int)m_textLines.size(); ++i )
	{
		if ( (totalchars - (int)m_textLines[i].length()) < 0 )
		{
			out = m_textLines[i].substr(0, totalchars) + "_";
			glPrint(FONT_CUTSCENE, x, y, out.c_str());
			break;
		}
		else
		{						
			if ( m_drawnLines < i+1 )
			{
				m_drawnLines = i+1;
				if ( (m_textLines.size() == i+1 && m_skipping) || (m_skipping == false) )
				m_pauseType = true;
			}
			
			if ( m_pauseType && i+1==m_drawnLines )
			{
				out = m_textLines[i].c_str();
				if ( (m_typet->get_ticks()/500)%2 ) out += "_";
				glPrint(FONT_CUTSCENE, x, y, out.c_str());
				break;
			}
			else
			{	
				glPrint(FONT_CUTSCENE, x, y, m_textLines[i].c_str());
				totalchars -= (int)m_textLines[i].length(); y += h;
				
				if ( i+1==m_textLines.size() )
				m_finished=true;
			}
		}
	}
	
	glPrint(FONT_TEXT, m_skm_x, m_skm_y, m_skm);

	SDL_Rect r;
	g_Fonts->get(FONT_ALIEN)->textSize( level_names[m_levelid].c_str(), &r );
	glPrint(FONT_ALIEN, (SCREEN_W/2) - (r.w/2), 32, level_names[m_levelid].c_str() );

	std::string lvlname = "* " + act_names[(int)(m_levelid/4)] + " *"; 

	g_Fonts->get(FONT_CUTSCENE)->textSize( lvlname.c_str(), &r );
	glPrint(FONT_CUTSCENE, (SCREEN_W/2) - (r.w/2), 32 - r.h - 2, lvlname.c_str() );

	drawAllRules();

	glDrawRect(32, 305, SCREEN_W - 64, 3, 1, 1, 1);
	glDrawRect(32, 55, SCREEN_W - 64, 3, 1, 1, 1);

	glDrawRect(0, 0, 8, SCREEN_H, 1, 1, 1);
	glDrawRect(0, 0, SCREEN_W, 8, 1, 1, 1);
	glDrawRect(SCREEN_W-8, 0, 8, SCREEN_H, 1, 1, 1);
	glDrawRect(0, SCREEN_H-8, SCREEN_W, 8, 1, 1, 1);
	
	glDisable2D();	
}
